﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Pets.Abstract;
using System.Reflection;
namespace Pets.Concrete
{
    /// <summary>
    /// Класс для загрузки\сохранения в хмл-файл
    /// </summary>
    public sealed class SerializerXML : Serializer
    {
        #region Методы интерфейса

        private List<string> out_list;

        private void PrintXMLToList(object obj, string parent_name)
        {
            if(out_list == null) out_list = new List<string>();
            string Name = string.Empty;
            if (parent_name == null)
            {
                Name = obj.GetType().ToString();
                int point_pos = Name.LastIndexOf('.');
                Name = Name.Substring(point_pos + 1, Name.Length - point_pos - 1);            
            }
            else
            {
                Name = parent_name;  
            }
            out_list.Add(TagDecorator.ToOpenTag(Name));
            ///Рекурсия
            foreach (System.Reflection.PropertyInfo property in obj.GetType().GetProperties())
            {
                object value = property.GetValue(obj, null);
                if (value.GetType() == typeof(int) || value.GetType() == typeof(string))
                {
                    out_list.Add(TagDecorator.ToDoubleTag(property.Name, value.ToString()));
                }
                else
                {
                    PrintXMLToList(value,property.Name);
                }
            }
            out_list.Add(TagDecorator.ToCloseTag(Name));

        }

        public override void Save(string source, List<Animal> list_animal)
        {

            /// <summary>
            /// Обявление потока для записи
            /// </summary>
            StreamWriter sw = new StreamWriter(source, false);

            /// <summary>
            /// Начало колекции Animals
            /// </summary>
            sw.WriteLine(TagDecorator.ToOpenTag("Animals"));
            foreach(Animal animal in list_animal)
            {
            out_list = null;
            PrintXMLToList(animal,null);
            foreach (string line in out_list) sw.WriteLine(line);
            }
            sw.WriteLine(TagDecorator.ToCloseTag("Animals"));
            sw.Close();

        }

        /// <summary>
        /// Загрузка колекции Animals из файла Source
        /// </summary>
        public  override List<Animal> Load(string source)
        {
            ///<summary>
            ///Запись из файла в список
            ///</summary>
            StreamReader sr = new StreamReader(source);
            List<string> tags_list = new List<string>();
            string line;

            while ((line = sr.ReadLine()) != null)
            {
                tags_list.Add(line);
            }

            ///<summary>
            ///Инициализация объектов
            ///</summary>
            List<Animal> animals = new List<Animal>();
            this.source = tags_list;
            this.isCheked = new bool[this.source.Count];
            int open_position, close_position;
            open_position = 1;
            close_position = open_position + 1;

            ///<summary>
            ///Циклический поиск Сat или Дог в списке
            ///</summary>
            while (close_position != tags_list.Count - 2)
            {
                ///<summary>
                ///Поиск  позиции в списке закрывающегося тега к текущему открытому
                ///</summary>
                close_position = TagDecorator.FindCloseTag(tags_list, open_position, TagDecorator.GetName(tags_list[open_position]));
                
                ///<summary>
                ///Построение хмл-дерева
                ///</summary>
                this.FindChildren(open_position, close_position, 1, null);
                
                ///<summary>
                ///Создание объектов Кэт или Дог и запись в массив
                ///</summary>
                Animal cat_or_dog = this.BuildTree(this.tree_tags) as Animal;
                animals.Add(cat_or_dog);
                open_position = close_position + 1;
            }
            return animals;
        }

        #endregion

        #region Приватные члены

        private ClassTag tree_tags;

        private const  int classes_in_solution = 3;
        
        ///<summary>
        ///True, если строка из хмл-файла просмотрена
        ///</summary>
        private bool[] isCheked;

        private List<string> source;

        ///<summary>
        /// Рекурсивное построение Хмл-дерева в памяти и занесение в переменную tree_tags 
        ///</summary>
        private void FindChildren(int start, int end, int lvl, ClassTag parent)
        {
            ///<summary>
            /// Начальная инициализация
            ///</summary>
            if (lvl == 1)
            {
                this.Intalize(start);
                isCheked[start] = true;
                isCheked[end] = true;
                this.FindChildren(start + 1, end - 1, 2, this.tree_tags);
            }
            
            ///<summary>
            /// Поиск дочерних єлементов
            ///</summary>
            for (int i = start; i <= end; i++)
            {
                if (isCheked[i] != true)
                {
                    isCheked[i] = true;
                    string name = TagDecorator.GetName(this.source[i]);
                    bool isClass = false;
                    
                    ///<summary>
                    ///Пренадлежность тега к классу\свойству
                    ///</summary>
                    for (int m = 0; m < classes_in_solution; m++)
                    {
                        ClassesSol class_sol = (ClassesSol)m;
                        if (class_sol.ToString() == name) isClass = true;
                    }
                    if (isClass)
                    {
                        ///<summary>
                        /// Присвоение свойств
                        ///</summary>
                        ClassTag this_tag = new ClassTag();
                        this_tag.Name = name;
                        this_tag.Level = lvl;
                        this_tag.Parent = parent;
                        if (parent.Children == null)
                        {
                            parent.Children = new List<ITag>();
                        }
                        parent.Children.Add(this_tag);
                        
                        ///<summary>
                        ///Поиск закрывающего тега для текущего
                        ///</summary>
                        int new_end = TagDecorator.FindCloseTag(this.source, i + 1, this_tag.Name);
                        isCheked[new_end] = true;
                        
                        ///<summary>
                        /// Рекурсивный поиск дочерних элементов текiущего тега
                        ///</summary>
                        this.FindChildren(i + 1, new_end - 1, lvl + 1, this_tag);
                    }
                    else
                    {
                        ///<summary>
                        /// Присвоение свойств
                        ///</summary>
                        PropTag this_tag = new PropTag();
                        this_tag.Name = name;
                        this_tag.Level = lvl;
                        this_tag.Parent = parent;
                        this_tag.Value = TagDecorator.GetValue(this.source[i]);
                        if (parent.Children == null)
                        {
                            parent.Children = new List<ITag>();
                        }
                        parent.Children.Add(this_tag);
                    }
                }
            }
        }
        
        ///<summary>
        ///Начальная инициализация хмл-дерева, создание корневого елемента
        ///</summary>
        private void Intalize(int open_tag_pos)
        {
            this.tree_tags = new ClassTag();
            this.tree_tags.Name = TagDecorator.GetName(this.source[open_tag_pos]);
            this.tree_tags.Level = 1;
            this.tree_tags.Parent = null;

            for (int i = open_tag_pos; i < isCheked.Length; i++) this.isCheked[i] = false;
        }

        #endregion

    }
}